package org.fjsei.yewu.graphql;

import graphql.PublicApi;
import graphql.relay.*;
import graphql.schema.DataFetchingEnvironment;

import java.util.List;

import static graphql.Assert.assertTrue;

/**Relay包装器功能
 * 关系数据库动态查询，或者 Elasticsearch 分页查询。
 * 前置条件：采取数据库分页后的场景。 无需要知道潜在的列表items[]的大小;
 * 数据库可支持任意大小的集合。最简单的小规模数据集请用MemoryListConnection；
 * Relay标准输入要用after,before,first,last 来。
 * 注意first配套after；last配套before；不能交叉配套。
 * Relay标准输出：
 * connection集合代理{edges【{cursor node}】; pageInfo{ hasPreviousPage  hasNextPage startCursor and endCursor}}
 * */
@PublicApi
public class DbPageConnection<T> extends MemoryListConnection<T> {
    private int offset;
    //向前查，若是比起limit还少就是到结尾了；
    //若倒着向后查的：若是offset比last|maxLimit小的，就是到了顶部。
    private boolean onlistTop=false;

    public DbPageConnection(String prefix, int maxLimit, DataFetchingEnvironment env) {
        assertTrue(prefix != null && !prefix.isEmpty(), () -> "prefix cannot be null or empty");
        this.prefix = prefix;
        this.limit=maxLimit;

        int afterOffset = getOffsetFromCursor(env.getArgument("after"), -1);
        int beforeOffset = getOffsetFromCursor(env.getArgument("before"), -1);

        Integer first = env.getArgument("first");
        Integer last = env.getArgument("last");
        //last配套before;
        if(last!=null && last>0)
        {
            assertTrue(beforeOffset >=0 , () -> "last要配套before");
            offset= beforeOffset-last;
            if(offset>=0)
                limit= Math.min(last, maxLimit);
            else {
                assertTrue(beforeOffset >0 , () -> "beforeOffset<=0");
                limit = beforeOffset;
                onlistTop=true;
            }
        }else{
            //first配套after； 缺省是first=top;
            offset= afterOffset+1;
            //默认3条
            limit=Math.min(first!=null? first:3, maxLimit);
        }
        //first:若强制配套before: 就代表最顶部的top [first];
        if(offset<0)    offset=0;
        if(offset==0)   onlistTop=true;
        assertTrue(limit >0 , () -> "limit cannot<=0");
        //assertTrue(limit<=maxLimit, () -> format("limit cannot > %d", maxLimit) );
    }

    public DbPageConnection(String prefix,  DataFetchingEnvironment env) {
        this(prefix,20,env);
    }
    public DbPageConnection(DataFetchingEnvironment env) {
        this(DUMMY_CURSOR_PREFIX,20,env);
    }
    /**对应的 偏移量分页模式
     * */
    public int getOffset() {
        return offset;
    }


    /**这里特别：
     * edges是单页面的，并非说的是全部的列表。PageInfo计算不一样了
     * */
    @Override
    public Connection<T> get(DataFetchingEnvironment environment) {
        List<Edge<T>> edges = buildEdges(offset);
        if (edges.size() == 0) {
            return emptyConnection();
        }
        //注意first配套after；实际返回edge数量达不到first|limit代表走到最后了。
        //last配套before；实际返回edge数量达不到last|limit代表跑到最前头了。
        Edge<T> firstEdge = edges.get(0);
        Edge<T> lastEdge = edges.get(edges.size() - 1);
        PageInfo pageInfo = new DefaultPageInfo(
                firstEdge.getCursor(),
                lastEdge.getCursor(),
                !onlistTop,
                edges.size()>=limit
        );
        return new DefaultConnection<>(
                edges,
                pageInfo
        );
    }

}

